package com.cts.aspect;

import com.cts.annotation.OperationLogger;
import com.cts.entity.ExceptionLog;
import com.cts.entity.OperationLog;
import com.cts.model.dto.LoginUser;
import com.cts.service.ExceptionLogService;
import com.cts.util.*;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * @projectName: CBlog
 * @package: com.cts.aspect
 * @className: ExceptionLogAspect
 * @author: chentongsheng
 * @description: TODO
 * @date: 2023/4/7 21:52
 * @version: 1.0
 */
@Component
@Aspect
@Slf4j
public class ExceptionLogAspect {

    @Autowired
    ExceptionLogService exceptionLogService;

    @Pointcut("execution(* com.cts.controller..*.*(..))")
    public void logPointcut() {
    }

    @AfterThrowing(value = "logPointcut()", throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint, Exception e) {
        ExceptionLog log = handleLog(joinPoint, e);
        exceptionLogService.save(log);
    }

    private ExceptionLog handleLog(JoinPoint joinPoint, Exception e) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String uri = request.getRequestURI();
        String method = request.getMethod();
        String description = getDescriptionFromAnnotations(joinPoint);
        String ip = IpUtils.getIpAddress(request);
        String error = getStackTrace(e);
        String ipSource = IpUtils.getCityInfo(ip);
        String userAgentHeader = request.getHeader("User-Agent");
        UserAgent userAgent = UserAgent.parseUserAgentString(userAgentHeader);
        String os = userAgent.getOperatingSystem().getName();
        String browser = userAgent.getBrowser().toString();

        ExceptionLog exceptionLog = new ExceptionLog(uri, method, description, ip, error, ipSource, userAgentHeader, os, browser);
        Map<String, Object> requestParams = AopUtils.getRequestParams(joinPoint);
        exceptionLog.setParam(StringUtils.substring(JacksonUtils.writeValueAsString(requestParams), 0, 2000));
        exceptionLog.setCreateTime(LocalDateTime.now());
        return exceptionLog;
    }

    private String getDescriptionFromAnnotations(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        OperationLogger operationLogger = method.getAnnotation(OperationLogger.class);
        if (operationLogger != null) {
            return operationLogger.value();
        }

        return "";
    }

    /**
     * 获取堆栈信息
     *
     * @param throwable 异常
     * @return
     */
    private static String getStackTrace(Throwable throwable) {
        StringWriter sw = new StringWriter();
        try (PrintWriter pw = new PrintWriter(sw)) {
            throwable.printStackTrace(pw);
            return sw.toString();
        }
    }
}
